home *** CD-ROM | disk | FTP | other *** search
/ Commodore Free 14 / Commodore_Free_Issue_14_2007_Commodore_Computer_Club.d64 / t.hexfiles 9 < prev    next >
File List  |  2023-02-26  |  12KB  |  405 lines

  1. u
  2. Hexfiles part 9
  3. By Jason Kelk
  4.  http://www.oldschool-gaming.com
  5.  
  6. Ah, there you are. I wondered where
  7. you'd got to. Ready to start playing
  8. with that new project I promised last
  9. issue then? Good, lets get on with it,
  10. shall we? We're going to write a little
  11. game. Don't worry, it's not going to be
  12. Armalyte or anything complex like that,
  13. we're just going to move a few sprites
  14. around for a starter. As usual, there
  15. is some source on the covermount, the
  16. filename is game_1.asm, so fire up your
  17. text editor & LOAD it in.
  18.  
  19. The majority of the source is
  20. variations on routines we've covered in
  21. the past so you should be happy with
  22. them, so instead of dissecting the
  23. entire program a line at a time I'm
  24. just going to cover points of interest.
  25.  
  26. So, starting from the top of the source
  27. & working down, the first stop is a
  28. routine called makespr which fills the
  29. last sprite of bank 0 ($3FC0 - $3FCF)
  30. with $FF to make it a solid block. This
  31. is just a temporary measure, eventually
  32. there will be real definitions but for
  33. now we just want to see where the
  34. sprites are. A little later on in the
  35. source (after the raster setup) is a
  36. loop called setsdp that points all
  37. eight sprites to $2800 for their data.
  38.  
  39. Right, now we have to set everything
  40. up. First, a quick call to xpand (which
  41. we'll cover in more detail soon), it is
  42. a routine that sets all sprite
  43. positions. Then comes something we've
  44. not used before:
  45.  
  46. lda #$00
  47. sta sync
  48. strtwait cmp sync
  49. beq strtwait
  50.  
  51. Now at first sight this seems totally
  52. pointless, doesn't it? If sync is set
  53. to $00 it's not suddenly going to
  54. change whilst we're sitting in a loop
  55. ... Well, actually it is because the
  56. second raster split, which is at line
  57. $FC, sets sync to $01 once a frame,
  58. this routine actually synchronises the
  59. runtime code with the raster, which is
  60. where the label gets it's name.
  61.  
  62. After that we read $D01E, which is the
  63. sprite to sprite collision register,
  64. but we don't do anything with the
  65. values since we're merely reading it in
  66. order to clear it for when the game
  67. starts.
  68.  
  69. Next up is the main loop of the game &
  70. it uses another loop to wait for sync
  71. to get the game movement synchronised
  72. to the raster interrupt; then we have
  73. three calls to subroutines, joyread
  74. reads the joystick in port 2, sprmove
  75. moves the sprite data &, again, xpand
  76. puts the sprite data into registers.
  77. After the main processes we have
  78. another read of $D01E but this time
  79. we're going to actually be doing
  80. something with the data. As with other
  81. sprite registers, $D01E represents each
  82. sprite with a bit, there are 8 sprites
  83. & 8 bits just like the sprite enable
  84. register $D015 (covered in part 4) but
  85. these bits become set if 2 sprites
  86. collide.
  87.  
  88. So if sprites 0 & 1 bump into each
  89. other, $D01E is set to $03 (sprite 0
  90. being the first bit & having a value of
  91. $01, sprite 1 being the 2nd bit &
  92. representing the value $02) & if
  93. sprites 0 & 7 have a pile-up, $D01E
  94. reads $81.
  95.  
  96. There is a problem with $D01E though;
  97. if sprites 0 & 3 hit each other & at
  98. the same time sprites 1 & 6 have a
  99. prang, we can only see that sprite 0
  100. has hit at least 1 of the others, not
  101. which one(s). In fact, if all 4 sprites
  102. hit each other the value in $D01E will
  103. be the same as when they collide as 2
  104. pairs so whilst that's not a problem
  105. for what we're doing right now (since
  106. we just need to know if sprite 0 has
  107. hit something) later on it will get in
  108. the way so this is only a temporary
  109. measure & we'll be dropping the use of
  110. hardware collisions fairly soon.
  111.  
  112. Anyway, back to the source & after that
  113. read from $D01E we have a new command
  114. called LSR, which means Logical Shift
  115. Right. LSR moves all the bits in a byte
  116. down one, so the highest bit, the one
  117. that represents 128 moves down to the
  118. 64 position, the 64 to the 32 & so
  119. forth.
  120.  
  121. The lowest bit, the one representing 1,
  122. falls off the end & into the carry flag
  123. & the highest bit is left un-set by
  124. this operation. LSR has a number of
  125. uses, not least of which is that it
  126. basically divides any 8 bit number by
  127. two. In our code we have an LSR A which
  128. is C64Asm's way of saying that the
  129. Logical Shift Right will happen to the
  130. value in the accumulator, but LSR $4000
  131. will work just as well & perform the
  132. operation on the appropriate byte in
  133. memory.
  134.  
  135. But why are we using LSR here? Well,
  136. it's a quick & dirty way of checking if
  137. the lowest bit of $D01E (the one
  138. representing sprite 0, our "player"
  139. sprite) is set. As I said, that low bit
  140. will fall into the carry flag & the BCC
  141. (Branch on Carry Clear) simply causes
  142. the program to move back to the label
  143. main if the carry flag is empty after
  144. that operation.
  145.  
  146. If the carry isn't empty that means
  147. sprite 0 is touching another sprite, so
  148. we do a quick INC $D027 to change the
  149. sprite colour for now to indicate that
  150. a collision has happened & then head
  151. back to main again.
  152.  
  153. The next part of the source is just the
  154. stock raster routines we've covered
  155. before, with raster2 setting sync to
  156. let the runtime code know it's time to
  157. start work before it calls $EA31. Then
  158. we arrive at xpand... this is the most
  159. complex routine in the entire piece of
  160. code, it takes the sprite co-ordinates
  161. from a table called sprtpos & puts them
  162. into $D000 onwards, but it does a
  163. little trickery to make handling the
  164. MSB easy for the rest of the program as
  165. well. This needs a complete breakdown
  166. of the code, but I need to cover 2 more
  167. new commands before that, ASL & ROR.
  168.  
  169. Accumulator Shift Left, or ASL to it's
  170. friends, is basically the reverse of
  171. LSR in that all the bits of a byte move
  172. up a place; the 1 bit is left un-set &
  173. the 128 bit gets nudged into the carry.
  174. And, since it's the opposite of LSR, it
  175. can also be thought of as multiplying
  176. an 8 bit number by two (although any-
  177. thing over $7f will need the 9th bit in
  178. the carry taken care of to get a
  179. correct answer). ROR, or ROtate Right,
  180. is almost the same as LSR, except that
  181. the previous contents of the carry get
  182. pushed into the 128 bit before the
  183. other end falls into it.
  184.  
  185. Both commands can work directly to the
  186. accumulator or to memory as with LSR.
  187. (There is a 4th command to this set,
  188. ROL, which works like ASL but has the
  189. contents of the carry pushed onto the
  190. end like ROR & we'll come across that
  191. at another time.)
  192.  
  193. Okay, so now for a line by line look at
  194. xpand. the sprtpos table contains 16
  195. bytes & they are stored as sprite 0's X
  196. & Y, sprite 1's X & Y & so forth, the
  197. same order as $D000 onwards uses. The
  198. 1st couple of lines just copy sprtpos+1
  199. (the 1st sprite's Y position) straight
  200. into the Y register: xpand ldx #$00 We
  201. know this...
  202.  
  203. xpndloop lda
  204. sprtpos+$01,X
  205. Read the Y co-ordinate
  206. sta $d001,x Set it into the
  207. sprite Y position
  208.  
  209. But the next bit to read sprtpos+$00
  210. (the first sprite's X position) is a
  211. little more fiddly:
  212.  
  213. lda sprtpos+$00,x Read X co-ordinate
  214. asl a Multiply by 2
  215.  
  216. Here we are using a trick that C=
  217. designers came up with ages ago (the
  218. same system is used for the X position
  219. of the light pen register). Because the
  220. screen is 320 pixels across we
  221. have to use the MSB (as explained in
  222. issue 10) but MSB handling is fiddly at
  223. the best of times. So we're taking the
  224. value in SPRTPOS & multiplying it by 2
  225. using ASL. But that doesn't actually
  226. sort out the MSB, does it? So...
  227.  
  228. ror $d010
  229. Roll $d010 a bit to the right
  230.  
  231. ...we move the contents of the carry
  232. into the top of the MSB (at the 128
  233. position). Because the loop runs 8
  234. times the first bit in ends up in the
  235. lowest position, the 2nd at the 2nd
  236. lowest & so on, until the bit that
  237. needs to represent sprite 7 is at the
  238. highest position. Then we...
  239.  
  240. sta $d000,X Write to sprite X position
  241.  
  242. ...write back the contents of the
  243. accumulator to the sprite.
  244.  
  245. Finally we manage the loop:
  246.  
  247. inx Just a standard loop counter, but
  248. inx we're going up in steps of2...
  249. cpx #$10 ...until X reaches $10
  250. bne xpndloop
  251.  
  252. And to finish, we count up in steps of
  253. 2 until we reach $10 & have, therefore,
  254. run the loop 8 times - since 2 bytes
  255. are transferred each iteration of the
  256. loop, that's all 16 bytes of sprtpos
  257. transferred during those 8 passes. This
  258. loop may seem a little complex, but MSB
  259. handling is a tricky job anyway &
  260. although it sacrifices some of our
  261. movement control horizontally, this
  262. technique means that from here onwards
  263. all sprite X co-ordinates are just a
  264. value from $00 to $FF so we can do
  265. quick & simple mathematics to them in
  266. order to shift the sprites; a simple
  267. INC to the first byte of sprpos once a
  268. frame will move the 1st sprite right
  269. all the way across the screen with no
  270. extra work needed. There are situations
  271. where it's necessary to make things
  272. move at a single pixel a frame, but for
  273. general useage, this goes & if it's
  274. good enough for C= design team it's
  275. good enough for us!
  276.  
  277. After the xpand routine comes joyread,
  278. a routine to scan the joystick. 1st up
  279. is a read from $DC00 & we're using LSR
  280. again to move the bits off into the
  281. carry one at a time to see what state
  282. they're in. $DC00 actually works in
  283. reverse, if the 1st bit is clear that
  284. means that the joystich has been pushed
  285. up so our sprite needs to react. How do
  286. we test that?
  287.  
  288. With a Branch on Carry Set (BCS)
  289. command; if the carry is set after the
  290. LSR then the stick isn't being pushed
  291. up & we branch over the routine that
  292. moves the sprite up so that it doesn't
  293. move that way. The same goes for the
  294. other directions & then FIRE. The 5
  295. joystick bits represent (lowest to
  296. highest) Up, Down, Left, Right & FIRE.
  297.  
  298. So we move the sprite up like this:
  299.  
  300. ldx sprtpos+$01 Read sprite Y position
  301. dex
  302. dex
  303. dex
  304. dex Subtract four
  305. cpx #$32 $32 is the top line of screen
  306. bcs setup
  307.  
  308. BCS again? Well yes, due to the way the
  309. 6510 does mathematics, BCS will act as
  310. a "greater or equal to" command here;
  311. if the contents of the X register are
  312. greater than or equal to $32 it'll
  313. branch to setup.
  314.  
  315. ldx #$32
  316.  
  317. Since this will only happen if X is
  318. less than $32 it makes sure X will
  319. always be $32 or greater, so not into
  320. the upper border.
  321.  
  322. setup stx sprtpos+$01
  323. Store the X position back
  324.  
  325. The BASIC equivalent to the CMP, BCS &
  326. LDX is IF Y<50 THEN Y=50
  327.  
  328. The down works in a similar manner,
  329. except that it's adding to the X
  330. register with four INXs & uses a BCC to
  331. branch over the LDX #$E5 (the lowest
  332. position a sprite can be at without
  333. being under the lower border) since BCC
  334. only works as a "less than" when used
  335. after the compare, not "less than or
  336. equal". (These tricks with BCC & BCS
  337. work after any compare command, not
  338. just those for the X register & can be
  339. very useful.)
  340.  
  341. Again, Left & Right are the same basic
  342. block of code as Up & Down, except that
  343. it has different stop positions for the
  344. edges of the screen &, because the X
  345. position of the sprites gets multiplied
  346. by 2 when they're displayed by xpand,
  347. the X position is only changed by 2
  348. rather than 4
  349.  
  350. Okay, we have one final loop to look
  351. at, sprmove takes the contents of
  352. sprtpos+$04 (sprite 3's X position) &
  353. adds the contents of another table
  354. called sprtspd to it, then goes through
  355. the sprite positions until it does
  356. sprite 7's Y position. Since sprtpos
  357. contains the present positions of the
  358. sprites & sprtspd contains their
  359. "speed" settings, the value that is
  360. added to each X or Y co-ordinate in
  361. turn to make the sprites move.
  362.  
  363. This sprtspd table uses some trickery,
  364. adding $FF, for example, to a byte will
  365. actually cause the byte to "wrap"
  366. around so the value goes down by 1.
  367. This is where xpand really comes into
  368. it's own, instead of having to know
  369. which direction the sprite is moving &
  370. have a routine to handle it we just
  371. perform an add! Sneaky, eh?
  372.  
  373. Righto, that's your lot for this
  374. installment, but next time we'll add
  375. some new features to our game, like
  376. some better collisions, an ingame
  377. soundtrack & some nice graphics. LOAD
  378. up game_2.asm to get the little game-
  379. ette going with a sprite & some backing
  380. music (as per how we did these things
  381. with the demo previously, so you should
  382. be able to work out for yourself how).
  383. As always, contact me with any
  384. questions & I'll see you next time,
  385. matey.
  386.  
  387. The source code for the routines above
  388. can be downloaded at:
  389.  
  390.  http://www.oldschoolgaming.com
  391.   /content/files/hex_files
  392.   /part_9_files.zip
  393.  
  394. for easier reference.
  395.  
  396. C= Free would like to thank Jason for
  397. granting permission to print the
  398. article.
  399.  
  400. Unfortunately at the moment this is the
  401. last article in the series, you could
  402. contact Jason & request more if you
  403. found the info useful.
  404.  
  405.